package com.stars.easyms.proxy;

import com.stars.easyms.base.util.*;
import com.stars.easyms.proxy.annotation.EasyMsProxyService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

/**
 * <p>className: EasyMsProxyServiceRegistrar</p>
 * <p>description: EasyMs代理初始化类</p>
 *
 * @author guoguifang
 * @version 1.7.3
 * @date 2021/3/23 11:08 上午
 */
class EasyMsProxyServiceRegistrar implements ImportBeanDefinitionRegistrar {

    private static final Logger logger = LoggerFactory.getLogger(EasyMsProxyServiceRegistrar.class);

    private static Map<Method, EasyMsProxyInfo> easyMsProxyInfoMap = Collections.emptyMap();

    static EasyMsProxyInfo getEasyMsProxyInfo(Method method) {
        return easyMsProxyInfoMap.get(method);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取所有加了EasyMsProxyService注解的类和接口
        Set<Class<?>> proxyServiceClassSet = ReflectUtil.getAllClassByAnnotation(Collections.emptySet(), EasyMsProxyService.class);
        proxyServiceClassSet.addAll(ReflectUtil.getAllClassByAnnotation(
                Collections.singleton(SpringBootUtil.getSpringApplicationPackageName()), EasyMsProxyService.class));
        if (proxyServiceClassSet.isEmpty()) {
            logger.warn("No valid @EasyMsProxyService class or interface was found！");
            return;
        }

        Map<Method, EasyMsProxyInfo> localEasyMsProxyInfoMap = new HashMap<>(32);

        // 遍历所有EasyMsProxyService类及接口
        for (Class<?> proxyServiceClass : proxyServiceClassSet) {

            // 再次判断，确定只扫描加了EasyMsProxyService注解的类
            if (!proxyServiceClass.isAnnotationPresent(EasyMsProxyService.class)) {
                continue;
            }

            EasyMsProxyService easyMsProxyService = proxyServiceClass.getAnnotation(EasyMsProxyService.class);

            String className = proxyServiceClass.getName();
            String target = resolve(easyMsProxyService.target());
            String url = getUrl(resolve(easyMsProxyService.url()));
            String path = getPath(resolve(easyMsProxyService.path()));

            BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(EasyMsProxyServiceFactoryBean.class);
            definitionBuilder.addPropertyValue("target", target);
            definitionBuilder.addPropertyValue("url", url);
            definitionBuilder.addPropertyValue("path", path);
            definitionBuilder.addPropertyValue("type", className);
            definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
            beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
            String alias = StringUtils.isNotBlank(easyMsProxyService.value()) ?
                    easyMsProxyService.value() : StringUtils.uncapitalize(proxyServiceClass.getSimpleName());
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

            // 遍历该类或接口中的所有方法
            Method[] proxyServiceMethods = proxyServiceClass.getDeclaredMethods();
            for (Method proxyServiceMethod : proxyServiceMethods) {
                // 如果加了RequestMapping注解则使用RequestMapping的path，否则使用方法名作为path
                String requestMappingPath = null;
                if (proxyServiceMethod.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = proxyServiceMethod.getAnnotation(RequestMapping.class);
                    if (requestMapping.path().length > 0) {
                        requestMappingPath = requestMapping.path()[0];
                    }
                }
                String subPath = path + getPath(StringUtils.isNotBlank(requestMappingPath) ? resolve(requestMappingPath) : proxyServiceMethod.getName());
                EasyMsProxyInfo easyMsProxyInfo = new EasyMsProxyInfo();
                easyMsProxyInfo.setMethodParameters(proxyServiceMethod.getParameters());
                easyMsProxyInfo.setReturnType(proxyServiceMethod.getGenericReturnType());
                easyMsProxyInfo.setTarget(target);
                easyMsProxyInfo.setUrl(url);
                easyMsProxyInfo.setPath(subPath);
                localEasyMsProxyInfoMap.put(proxyServiceMethod, easyMsProxyInfo);
            }
        }

        easyMsProxyInfoMap = Collections.unmodifiableMap(localEasyMsProxyInfoMap);
    }

    private String resolve(String value) {
        if (StringUtils.isNotBlank(value)) {
            return PropertyPlaceholderUtil.replace(value);
        }
        return value;
    }

    private String getUrl(String url) {
        if (StringUtils.isNotBlank(url)) {
            if (!url.contains("://")) {
                url = "http://" + url;
            }
            try {
                new URL(url);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException(url + " is malformed", e);
            }
        }
        return url;
    }

    private String getPath(String path) {
        if (StringUtils.isNotBlank(path)) {
            path = path.trim();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (path.endsWith("/")) {
                path = path.substring(0, path.length() - 1);
            }
        }
        return path;
    }

}
